import { TokenType } from '~/constants/TokenType'
import { Account } from '~/db'
import { singleton } from '~/libs/singleton'
import * as api from '~/libs/wx/api'
import { TokenManager } from './token-manager'

export async function getComponentVerifyTicket(component_app_id: string): Promise<string> {
    const token = await TokenManager.get(TokenType.ComponentVerifyTicket, component_app_id)
    return token ?? ''
}

export async function getComponentAccessToken(component_app_id: string, forceNew = false): Promise<string | undefined> {
    let token = forceNew ? undefined : await TokenManager.get(TokenType.AccessToken, component_app_id)
    if (!token) {
        //通过接口获取
        const newToken = await singleton(`getComponentAccessToken:${component_app_id}`, async () => {
            const ticket = await getComponentVerifyTicket(component_app_id)
            const account = await Account.findOne({
                where: {
                    app_id: component_app_id,
                    component_app_id: '',
                },
            })
            if (!account) return
            return api.component.getComponentAccessToken(component_app_id, account.secret!, ticket)
        })

        if (newToken) {
            await TokenManager.set({
                app_id: component_app_id,
                component_app_id: '',
                type: TokenType.AccessToken,
                token: newToken.component_access_token,
                expire_time: new Date(Date.now() + newToken.expires_in * 1000),
            })
            return newToken.component_access_token
        }
    }
    return token
}

export async function getAccessToken(
    app_id: string,
    component_app_id: string,
    forceNew = false,
): Promise<string | undefined> {
    let token = forceNew ? undefined : await TokenManager.get(TokenType.AccessToken, app_id, component_app_id)
    if (!token) {
        const account = await Account.findOne({
            where: {
                app_id,
                component_app_id,
            },
        })
        if (!account) return

        //通过接口获取
        const newToken = await singleton(`getAccessToken:${app_id}:${component_app_id}`, async () => {
            if (component_app_id) {
                const component_access_token = await getComponentAccessToken(component_app_id)
                if (!component_access_token) return
                const ret = await api.component.refreshAuthorizerAccessToken(
                    component_app_id,
                    component_access_token,
                    app_id,
                    account.extra.refresh_token,
                )
                return {
                    access_token: ret.authorizer_access_token,
                    expires_in: ret.expires_in,
                    refresh_token: ret.authorizer_refresh_token,
                }
            } else {
                return
            }
        })

        if (newToken) {
            await TokenManager.set({
                app_id,
                component_app_id,
                type: TokenType.AccessToken,
                token: newToken.access_token,
                expire_time: new Date(Date.now() + newToken.expires_in * 1000),
            })
            if (newToken.refresh_token) {
                account.extra = {
                    ...account.extra,
                    refresh_token: newToken.refresh_token,
                }
                account.save()
            }
            return newToken.access_token
        }
    }

    return token
}

export async function getAccessTokenWithExpires(app_id: string, component_app_id: string, forceNew = false) {
    let token = forceNew
        ? undefined
        : await TokenManager.getWithExpires(TokenType.AccessToken, app_id, component_app_id)
    if (!token) {
        const account = await Account.findOne({
            where: {
                app_id,
                component_app_id,
            },
        })
        if (!account) return

        //通过接口获取
        const newToken = await singleton(`getAccessToken:${app_id}:${component_app_id}`, async () => {
            if (component_app_id) {
                const component_access_token = await getComponentAccessToken(component_app_id)
                if (!component_access_token) return
                const ret = await api.component.refreshAuthorizerAccessToken(
                    component_app_id,
                    component_access_token,
                    app_id,
                    account.extra.refresh_token,
                )
                return {
                    access_token: ret.authorizer_access_token,
                    expires_in: ret.expires_in,
                    refresh_token: ret.authorizer_refresh_token,
                }
            } else {
                return
            }
        })

        if (newToken) {
            await TokenManager.set({
                app_id,
                component_app_id,
                type: TokenType.AccessToken,
                token: newToken.access_token,
                expire_time: new Date(Date.now() + newToken.expires_in * 1000),
            })
            if (newToken.refresh_token) {
                account.extra = {
                    ...account.extra,
                    refresh_token: newToken.refresh_token,
                }
                account.save()
            }
            return {
                token: newToken.access_token,
                expires: newToken.expires_in,
            }
        }
    }

    return token
}

export async function getJsApiTicket(
    app_id: string,
    component_app_id: string,
    forceNew = false,
): Promise<string | undefined> {
    let token = forceNew ? undefined : await TokenManager.get(TokenType.JsapiTicket, app_id, component_app_id)
    if (!token) {
        //通过接口获取
        const newToken = await singleton(`getJsApiTicket:${app_id}:${component_app_id}`, async () => {
            const access_token = await getAccessToken(app_id, component_app_id)
            if (!access_token) return
            return api.web.getJsApiTicket(access_token)
        })

        if (newToken) {
            await TokenManager.set({
                app_id,
                component_app_id,
                type: TokenType.JsapiTicket,
                token: newToken.ticket,
                expire_time: new Date(Date.now() + newToken.expires_in * 1000),
            })
            return newToken.ticket
        }
    }

    return token
}
